#pragma once

#include <memory>
#include <vector>


namespace evm {

using namespace std;

/* A simple timer/stopwatch helper class.
 * Not thread-safe (when a single watch is mutated by multiple threads at
 * the same time). Thread-safe when used by a single thread (not shared) or
 * when operations are performed in a thread-safe manner on these objects by
 * wrapping those operations with locks.
 */
struct StopWatch {
    /* Array of splitting points of pair (elapsed, split length).
     */
    using Split = pair<long, long>;
    using Splits = vector<Split>;

    /* Duration in milliseconds.
     * If -1, do not stop forever.
     */
    StopWatch(long duration = -1, bool start = true)
        : duration_(duration) {
        if (start)
            this->start();
    }

    /* Starts the watch (if not already started).
     * It resets any splits previously captured (if any).
     */
    void start();

    /* Stops the watch. */
    void stop();

    /* Restarts the watch from a started/stopped state. */
    void restart();

    /* Resumes the watch from a stopped state. */
    void resume();

    bool started();

    bool stopped();

    /* Returns how many milliseconds have elapsed. */
    long elapsed(long maximum = -1);

    /* Returns how many milliseconds are left until the watch expires.
     * If duration is infinite, returns -1;
     */
    long leftover();

    /* Returns if the watch has expired (ie, duration provided elapsed). */
    bool expired();

    /* Captures a split/elapsed since start time (and doesn't stop). */
    const Split& split();

    /* Accessor to all/any splits that have been captured. */
    const Splits& splits();

private:
    static long now();

    const long duration_;

    bool started_{};

    long started_at_{};
    long stopped_at_{};

    Splits splits_;
};

}
